package wci.frontend.pascal.parsers;

import static wci.frontend.pascal.PascalErrorCode.MISSING_SEMICOLON;
import static wci.frontend.pascal.PascalErrorCode.UNEXPECTED_TOKEN;
import static wci.frontend.pascal.PascalTokenType.BEGIN;
import static wci.frontend.pascal.PascalTokenType.CASE;
import static wci.frontend.pascal.PascalTokenType.DOT;
import static wci.frontend.pascal.PascalTokenType.ELSE;
import static wci.frontend.pascal.PascalTokenType.END;
import static wci.frontend.pascal.PascalTokenType.FOR;
import static wci.frontend.pascal.PascalTokenType.IDENTIFIER;
import static wci.frontend.pascal.PascalTokenType.REPEAT;
import static wci.frontend.pascal.PascalTokenType.SEMICOLON;
import static wci.frontend.pascal.PascalTokenType.UNTIL;
import static wci.frontend.pascal.PascalTokenType.WHILE;
import static wci.intermediate.icodeimpl.ICodeKeyImpl.LINE;
import static wci.intermediate.icodeimpl.ICodeNodeTypeImpl.NO_OP;

import java.util.EnumSet;

import wci.frontend.EofToken;
import wci.frontend.Token;
import wci.frontend.TokenType;
import wci.frontend.pascal.PascalErrorCode;
import wci.frontend.pascal.PascalParserTD;
import wci.frontend.pascal.PascalTokenType;
import wci.intermediate.Definition;
import wci.intermediate.ICodeFactory;
import wci.intermediate.ICodeNode;
import wci.intermediate.SymTabEntry;
import wci.intermediate.symtabimpl.DefinitionImpl;

/**
 * <h1>StatementParser</h1>
 * <p>解析一个Pascal语句，目前它可能是一个复合语句，有有可能是一个赋值语句</p>
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class StatementParser extends PascalParserTD
{
    /**
     * 代表开始一条语句的同步集合，意思是如果当前token满足其中任何类型，就可以开始一条新语句了。
     */
    protected static final EnumSet<PascalTokenType> STMT_START_SET =
        EnumSet.of(BEGIN, CASE, FOR, PascalTokenType.IF, REPEAT, WHILE,
                   IDENTIFIER, SEMICOLON);

    /**
     * 代表一条语句之后可以允许的token类型集合。
     */
    protected static final EnumSet<PascalTokenType> STMT_FOLLOW_SET =
        EnumSet.of(SEMICOLON, END, ELSE, UNTIL, DOT);
    public StatementParser(PascalParserTD parent)
    {
        super(parent);
    }

    /**
     * 解析一个语句，以传入的token判断该进行何种解析，子类会覆盖此方法
     * @param token 语句的第一个token
     * @return 分析子树的根节点
     * @throws Exception 
     */
    public ICodeNode parse(Token token)
        throws Exception
    {
        ICodeNode statementNode = null;

        switch ((PascalTokenType) token.getType()) {

        	//复合语句总是以begin开头，如"begin xxxx end." 
            case BEGIN: {
                CompoundStatementParser compoundParser =
                    new CompoundStatementParser(this);
                statementNode = compoundParser.parse(token);
                break;
            }

            //类似于 a = b 之类的赋值语句，这里a就是标识符identifier
            case IDENTIFIER: {
            	String name = token.getText().toLowerCase();
                SymTabEntry id = symTabStack.lookup(name);
                Definition idDefn = id != null ? id.getDefinition()
                                               : DefinitionImpl.UNDEFINED;

                // 赋值语句或是程式
                switch ((DefinitionImpl) idDefn) {

                    case VARIABLE:
                    case VALUE_PARM:
                    case VAR_PARM:
                    case UNDEFINED: {
                        AssignmentStatementParser assignmentParser =
                            new AssignmentStatementParser(this);
                        statementNode = assignmentParser.parse(token);
                        break;
                    }

                    case FUNCTION: {
                        AssignmentStatementParser assignmentParser =
                            new AssignmentStatementParser(this);
                        statementNode =
                            assignmentParser.parseFunctionNameAssignment(token);
                        break;
                    }

                    case PROCEDURE: {
                        CallParser callParser = new CallParser(this);
                        statementNode = callParser.parse(token);
                        break;
                    }

                    default: {
                        errorHandler.flag(token, UNEXPECTED_TOKEN, this);
                        token = nextToken();  //跳过标识符
                    }
                }
            }
            //5种控制语句
            case REPEAT: {
                RepeatStatementParser repeatParser =
                    new RepeatStatementParser(this);
                statementNode = repeatParser.parse(token);
                break;
            }
            case WHILE: {
                WhileStatementParser whileParser =
                    new WhileStatementParser(this);
                statementNode = whileParser.parse(token);
                break;
            }
            case FOR: {
                ForStatementParser forParser = new ForStatementParser(this);
                statementNode = forParser.parse(token);
                break;
            }
            case IF: {
                IfStatementParser ifParser = new IfStatementParser(this);
                statementNode = ifParser.parse(token);
                break;
            }
            case CASE: {//类似Java/C的switch
                CaseStatementParser caseParser = new CaseStatementParser(this);
                statementNode = caseParser.parse(token);
                break;
            }
            default: {
                statementNode = ICodeFactory.createICodeNode(NO_OP);
                break;
            }
        }

        // 设置根节点“行位置”属性，即第一个token的行位置
        setLineNumber(statementNode, token);

        return statementNode;
    }

    /**
     * 设置节点的行属性。PS: 我认为这种写法很怪，为何不setLineNumber(ICodeNode, int)?
     * @param node 分析树节点
     * @param token token
     */
    protected void setLineNumber(ICodeNode node, Token token)
    {
        if (node != null) {
            node.setAttribute(LINE, token.getLineNumber());
        }
    }

    /**
     * 解析语句列表，比如 a=b; b=c; c=d; 
     * @param token 当前token
     * @param parentNode 父节点即包含此语句列表的节点
     * @param terminator 结束此列表的token类型，一般为END
     * @param errorCode 错误码
     * @throws Exception
     */
    protected void parseList(Token token, ICodeNode parentNode,
                             PascalTokenType terminator,
                             PascalErrorCode errorCode)
        throws Exception
    {
    	 EnumSet<PascalTokenType> terminatorSet = STMT_START_SET.clone();
         terminatorSet.add(terminator);
        //遍历每条语句直到遇见结束TOKEN类型或文件结束
        while (!(token instanceof EofToken) &&
               (token.getType() != terminator)) {

            // 解析一条语句
            ICodeNode statementNode = parse(token);
            //语句子树根节点作为子节点附加到父节点上
            parentNode.addChild(statementNode);

            token = currentToken();
            TokenType tokenType = token.getType();

            //每条语句之后肯定是一个分号 ; 否则报错
            if (tokenType == SEMICOLON) {
                token = nextToken();  
            } else if (STMT_START_SET.contains(tokenType)) {
                errorHandler.flag(token, MISSING_SEMICOLON, this);
            }
            token = synchronize(terminatorSet);
        }

        //判断是否已到结束token，如果是就跳过它，否则报错
        if (token.getType() == terminator) {
            token = nextToken();  // consume the terminator token
        }else {
            errorHandler.flag(token, errorCode, this);
        }
    }
}
